// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include "machine.h"

//----------------------------------------------------------------------
// AdvancePC
// 	Make PC count one
//----------------------------------------------------------------------

void AdvancePC()
{
  int pc;

  pc = machine->ReadRegister(PCReg);
  machine->WriteRegister(PrevPCReg, pc);
  pc = machine->ReadRegister(NextPCReg);
  machine->WriteRegister(PCReg, pc);
  pc += 4;
  machine->WriteRegister(NextPCReg, pc);
}

//----------------------------------------------------------------------
// ExceptionHandler
// 	Entry point into the Nachos kernel.  Called when a user program
//	is executing, and either does a syscall, or generates an addressing
//	or arithmetic exception.
//
// 	For system calls, the following is the calling convention:
//
// 	system call code -- r2
//		arg1 -- r4
//		arg2 -- r5
//		arg3 -- r6
//		arg4 -- r7
//
//	The result of the system call, if any, must be put back into r2. 
//
// And don't forget to increment the pc before returning. (Or else you'll
// loop making the same system call forever!
//
//	"which" is the kind of exception.  The list of possible exceptions 
//	are in machine.h.
//----------------------------------------------------------------------

void
ExceptionHandler(ExceptionType which)
{
    int type = machine->ReadRegister(2);
    if (which == SyscallException)
    {
	if (type == SC_Halt) {
		DEBUG('a', "Shutdown, initiated by user program.\n");
		interrupt->Halt();
	}
	else if(type==SC_Create){
		DEBUG('a', "Create, initiated by user program.\n");
		printf("Exception Create\n");
		int fileNameAddr=(int)machine->ReadRegister(4);
		char* fileName=new char[256];
		int ch=-1;

		int fileNameI;
		for (fileNameI=0;ch!=0&&fileNameI<256;++fileNameI)
		{
		  machine->ReadMem(fileNameAddr+fileNameI,1,&ch);
		  fileName[fileNameI]=(char)ch;
		}
		ASSERT(fileNameI<256);
		printf("name: %s\n",fileName);

		interrupt->Create(fileName);

		AdvancePC();
	}
	else if(type==SC_Open){
		DEBUG('a', "Open, initiated by user program.\n");
		printf("Exception Open\n");
		int fileNameAddr=(int)machine->ReadRegister(4);
		char* fileName=new char[256];
		int ch=-1;
		int fileNameI;
		for (fileNameI=0;ch!=0&&fileNameI<256;++fileNameI) {
		  machine->ReadMem(fileNameAddr+fileNameI,1,&ch);
		  fileName[fileNameI]=(char)ch;
		}
		ASSERT(fileNameI<256);
		printf("name: %s\n",fileName);
		int OpenfileId;
		OpenfileId=interrupt->Open(fileName);
		printf("OpenfileId: %d\n",OpenfileId);
		machine->WriteRegister(2,OpenfileId);
		AdvancePC();
	}
	else if(type==SC_Close){
		DEBUG('a', "Close, initiated by user program.\n");
		printf("Exception Close\n");
		int id=machine->ReadRegister(4);
		interrupt->Close(id);
		AdvancePC();
	}
	else if(type==SC_Write){
		DEBUG('a', "Write, initiated by user program.\n");
		printf("Exception Write\n");
		int bufferIndex=(int)machine->ReadRegister(4);

		int size=machine->ReadRegister(5);
		int id=machine->ReadRegister(6);
		interrupt->Write(bufferIndex, size, id);
		AdvancePC();
	}
	else if(type==SC_Read){
		DEBUG('a', "Read, initiated by user program.\n");
		printf("Exception Read\n");
		int bufferIndex=(int)machine->ReadRegister(4);
		int size=machine->ReadRegister(5);
		int id=machine->ReadRegister(6);
		interrupt->Read(bufferIndex, size, id);
		AdvancePC();
	}
	else if(type==SC_Exec){
		DEBUG('a', "Exec, initiated by user program.\n");
		printf("Exception Exec\n");
		int SpaceId;
		int SC_Exec_name_addr=(int)machine->ReadRegister(4);
		//程序名
		char* SC_Exec_name=new char[256];
		int SC_Exec_ch=-1;
		int SC_Exec_name_i;
		for (SC_Exec_name_i=0;SC_Exec_ch!=0&&SC_Exec_name_i<256;++SC_Exec_name_i) {
		  machine->ReadMem(SC_Exec_name_addr+SC_Exec_name_i,1,&SC_Exec_ch);
		  SC_Exec_name[SC_Exec_name_i]=(char)SC_Exec_ch;
		}
		ASSERT(SC_Exec_name_i<256);
		printf("name: %s\n",SC_Exec_name);
		SpaceId=interrupt->Exec(SC_Exec_name);
		machine->WriteRegister(2,SpaceId);
		printf("SpaceId:%d\n",SpaceId);
		AdvancePC();
	}
	else if(type==SC_Join){
		DEBUG('a', "Join, initiated by user program.\n");
		printf("Exception fork\n");
		int SpaceId=(int)machine->ReadRegister(4);
		interrupt->Join(SpaceId);
		AdvancePC();
	}
	else if(type==SC_Exit){
		DEBUG('a', "Shutdown, initiated by user program.\n");
		printf("Exception Exit\n");
		int status=machine->ReadRegister(4);
		printf("status %d\n",status);
		interrupt->Exit(status);
		AdvancePC();
	}
	else if (type==SC_Fork)
	{
		DEBUG('a', "Fork, initiated by user program.\n");
		printf("Exception Fork\n");
		interrupt->Fork();
		AdvancePC();
	}
	else if(type==SC_Yield){
		DEBUG('a', "Yield, initiated by user program.\n");
		printf("Exception Yield\n");
		interrupt->Yield();
		AdvancePC();
	}
    }
    else if(which == TLBPageFaultException){
        int virtAddr=machine->registers[BadVAddrReg];
        int vpn = (unsigned) virtAddr / PageSize;
        printf("PageFault:CurrentThread VirtualPage %d fault...\n",vpn);
        //printf("Current free physicalPage count is%d\n",machine->MemPageTable->NumClear());
	if (vpn >= machine->pageTableSize) {
	    DEBUG('a', "virtual page # %d too large for page table size %d!\n", 
			virtAddr, machine->pageTableSize);
	    machine->RaiseException(AddressErrorException, virtAddr);
	    return ;
	} else if (!machine->pageTable[vpn].valid) {
	    DEBUG('a', "virtual page # %d too large for page table size %d!\n", 
			virtAddr, machine->pageTableSize);
	    machine->RaiseException(PageFaultException, virtAddr);
	    return ;
	}
	//Found in pageTable!
	//FIFO in TLB
	machine->tlb[machine->stepOfTLB].virtualPage=vpn;
	machine->tlb[machine->stepOfTLB].physicalPage=machine->pageTable[vpn].physicalPage;
	machine->tlb[machine->stepOfTLB].valid=true;
	machine->tlb[machine->stepOfTLB].use=true;
	machine->stepOfTLB++;
	if(TLBSize==machine->stepOfTLB)
	    machine->stepOfTLB=0;
    }
    else if(which == PageFaultException){
       stats->numPageFaults++;
       int i;
       int virtAdd=machine->registers[BadVAddrReg];
       int vpn = (unsigned) virtAdd / PageSize;
       printf("PageFault:CurrentThread VirtualPage %d fault...\n",vpn);
       printf("Current free physicalPage count is%d\n",machine->MemPageTable->NumClear());
       int phyPage=NumPhysPages-1;
       Thread* pt=NULL;
       //查找是否有空白页
       int fbn=machine->MemPageTable->Find();
       if(fbn!=-1)
       {
           phyPage=fbn;
           machine->MemPageTable->Set(fbn);
       }
       else
       {
	   //default is last PhysPages
           pt=(Thread*)machine->MemPageTable->GetOwner(NumPhysPages-1);
           //根据LRU算法寻找置换页
           int mintime=machine->MemPageTable->GetTime(NumPhysPages-1);

           for(i=NumPhysPages-1;i>=0;i--)
           {
               if(machine->MemPageTable->GetTime(i)<=mintime)
               {
printf("gettime %d \t%d\n",mintime,i);
                   phyPage=i;//phyPageNum which will be replace from pageTable
                   mintime=machine->MemPageTable->GetTime(i);
                   pt=(Thread*)machine->MemPageTable->GetOwner(i);//thread whose phyPage will be replaced from pageTable
               }
           }
       }
       //建立内存块缓存
       char buffer[PageSize];//temply save the content in which LRU order to replace
       char buffer1[PageSize];//currentThread's space memory
       for(i=0;i<PageSize;i++)
       {
           buffer[i]=machine->mainMemory[phyPage*PageSize+i];//copy the phyPage's content to buffer
       }
       //查找目标虚存页
       int virtPage=currentThread->space->pageTable[vpn].virtualPage;//currentThread's vpg
       machine->vm->Read(virtPage,buffer1);//set currentThread's space memory to buffer1
       for(i=0;i<PageSize;i++)
       {
           machine->mainMemory[phyPage*PageSize+i]=buffer1[i];//move currentThread's space memory to phyMem
       }
       machine->MemPageTable->SetCounter(phyPage,0);
       machine->MemPageTable->SetTime(phyPage,stats->totalTicks);
//printf("settime %d\n",stats->totalTicks);
       machine->MemPageTable->SetOwner(phyPage,(int*)currentThread);
       if(pt!=NULL)//if the thread owned phyPage that replaced by LRU is not Null
       {
           machine->vm->Write(virtPage,buffer);//move to vm
           int index=0;
           for(i=0;i<pt->space->numPages;i++)
           {
               if(pt->space->pageTable[i].physicalPage==phyPage)//find thread's pageTable item
                   index=i;
           }
          ASSERT(pt->space->pageTable[index].physicalPage==phyPage);
          //写回//set pt'space can not be mirror with phyPage
           pt->space->pageTable[index].valid=false;
           pt->space->pageTable[index].use=false;
           pt->space->pageTable[index].dirty=false;
           pt->space->pageTable[index].virtualPage=virtPage;
       }
       //写入//set currentThread'space can be mirror with phyPage
       currentThread->space->pageTable[vpn].valid=true;
       currentThread->space->pageTable[vpn].use=false;
       currentThread->space->pageTable[vpn].dirty=false;
       currentThread->space->pageTable[vpn].physicalPage=phyPage;
       if(fbn==-1)
           printf("***LRU算法*** \n Exchanging PhysicalPage phyPage:%d withVirtualPage:%d \n",phyPage,virtPage);
       else
           printf("Loading into PhysicalPage phyPage:%d from VirtualPage:%d\n",phyPage,virtPage);
    }
    else {
	printf("Unexpected user mode exception %d %d\n", which, type);
	ASSERT(FALSE);
    }
}

